Container的職責在於創建、配置與組裝bean,昨天我們學到了
如何使用@Value的用法、完全註解開發、泛型依賴注入
今日將進入新的領域AOP,今日我們先談談AOP是在什麼樣的場景出現的吧
看看我們怎麼為計算器加入log
public interface Calculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
我們可能會用System.out.println,但我們會發現要改log的format要改四個地方@@
public class MyCalcultor implements Calculator{
@Override
public int add(int i, int j) {
System.out.println("add method start,parameter:"+i +","+j);
int result = i+j;
System.out.println("add method end,result:"+result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("sub method start,parameter:"+i +","+j);
int result = i-j;
System.out.println("sub method end,result:"+result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("mul method start,parameter:"+i +","+j);
int result = i*j;
System.out.println("mul method end,result:"+result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("div method start,parameter:"+i +","+j);
int result = i/j;
System.out.println("div method end,result:"+result);
return result;
}
}
我們可能會想到寫一個LogUtils來取代System.out.println出現的位置
public class LogUtils {
public static void logStart(String methodName,Object...args){
System.out.println(methodName+" method start,parameter:"+ Arrays.asList(args));
}
public static void logEnd(String methodName,Object result){
System.out.println(methodName+" method end,result:"+ result);
}
}
然後你可能還會想要在方法的前後加入開始與結束時間,看一下效能
public class MyCalcultor implements Calculator{
@Override
public int add(int i, int j) {
LogUtils.logStart("add",i,j);
long startTime = System.currentTimeMillis();
int result = i+j;
LogUtils.logEnd("add",result);
long endTime = System.currentTimeMillis();
System.out.println("add method execute:"+(endTime - startTime));
return result;
}
@Override
public int sub(int i, int j) {
LogUtils.logStart("add",i,j);
long startTime = System.currentTimeMillis();
int result = i-j;
LogUtils.logEnd("add",result);
return result;
}
@Override
public int mul(int i, int j) {
LogUtils.logStart("add",i,j);
long startTime = System.currentTimeMillis();
int result = i*j;
LogUtils.logEnd("add",result);
long endTime = System.currentTimeMillis();
System.out.println("add method execute:"+(endTime - startTime));
return result;
}
@Override
public int div(int i, int j) {
LogUtils.logStart("add",i,j);
long startTime = System.currentTimeMillis();
int result = i/j;
LogUtils.logEnd("add",result);
long endTime = System.currentTimeMillis();
System.out.println("add method execute:"+(endTime - startTime));
return result;
}
}
然後...是不是可以再增加稽核軌跡看一下是那個user執行這個作業。然後你就會發現程式都糊再一起,真正的商業邏輯都被淹沒了。那有沒有方法能夠只保留商業邏輯像面這樣呢,而且還能保留log、稽核軌跡、效能紀錄呢,答案是可以的,就透過AOP來達成。
public class MyCalcultor implements Calculator{
@Override
public int add(int i, int j) {
int result = i+j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i-j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i*j;
return result;
}
@Override
public int div(int i, int j) {
int result = i/j;
return result;
}
}
面向切面編程,指在程式運行期間,將某段代碼動態的切入到指定的方法跟指定位置進行運作。面向能夠模組化我們關注的點,例如transaction management
要能達到將代碼動態切入到指定方法跟位置執行,需要透過動態代理的方式達成。透過代理對象在invoke對象方法前後就可以輕易加入想要執行的動作。
圖片來源:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying
AOP底層的動態代理有兩種方式
JDK動態代理範例
public class CalculatorProxy {
public static Calculator getProxy(final Calculator calculator){
InvocationHandler invokHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LogUtils.logStart(method.getName(),args);
Object result = method.invoke(calculator,args);
LogUtils.logEnd(method.getName(),result);
return result;
}
};
Class<?>[] interfaces = calculator.getClass().getInterfaces();
ClassLoader loder =calculator.getClass().getClassLoader();
Object proxy = Proxy.newProxyInstance(loder,interfaces,invokHandler);
return (Calculator)proxy;
}
}
@Test
public void testDay17(){
Calculator proxy = CalculatorProxy.getProxy(new MyCalcultor());
proxy.add(1,5);
}
Result